﻿using System;
using System.ComponentModel;
using System.Drawing;
using WindowsAPI.Hooks;
using WindowsAPI.Windows;

namespace WindowsAPI
{
    [Serializable]
    [DefaultEvent("KeyDown"), DefaultProperty("Enable")]
    [ToolboxBitmap(typeof(KeyboardComponent))]
    public partial class KeyboardComponent : Component, INotifyPropertyChanged
    {
        /* --------------------------------------------------------------------------------------------- */

        #region События

        [PropertyTab("test")]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Description("Событие возникает при изменении свойства объекта")]
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string PropertyName) { OnPropertyChanged(new PropertyChangedEventArgs(PropertyName)); }
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var lv_Handlers = PropertyChanged;
            if(lv_Handlers != null) lv_Handlers.Invoke(this, e);
        }

        /// <summary>Событие возникает при отпускании системной клавиши</summary>
        [Category("Keyboard"), Description("Событие возникает при отпускании системной клавиши")]
        public event OnKeyboardEventHandler SysKeyUp;
        protected virtual void OnSysKeyUp(OnKeyboardEventArgs Args)
        {
            var lv_Handlers = SysKeyUp;
            if(lv_Handlers != null) lv_Handlers.Invoke(this, Args);
            OnKeyUp(Args);
        }

        /// <summary>Событие возникает при нажатии системной клавиши</summary>
        [Category("Keyboard"), Description("Событие возникает при нажатии системной клавиши")]
        public event OnKeyboardEventHandler SysKeyDown;
        protected virtual void OnSysKeyDown(OnKeyboardEventArgs Args)
        {
            var lv_Handlers = SysKeyDown;
            if(lv_Handlers != null) lv_Handlers.Invoke(this, Args);
            OnKeyDown(Args);
        }

        /// <summary>Событие возникает при отпускании клавиши</summary>
        [Category("Keyboard"), Description("Событие возникает при отпускании клавиши")]
        public event OnKeyboardEventHandler KeyUp;
        protected virtual void OnKeyUp(OnKeyboardEventArgs Args)
        {
            var lv_Handlers = KeyUp;
            if(lv_Handlers != null) lv_Handlers.Invoke(this, Args);
        }

        /// <summary>Событие возникает при нажатии клавиши</summary>
        [Category("Keyboard"), Description("Событие возникает при нажатии клавиши")]
        public event OnKeyboardEventHandler KeyDown;
        protected virtual void OnKeyDown(OnKeyboardEventArgs Args)
        {
            var lv_Handlers = KeyDown;
            if(lv_Handlers != null) lv_Handlers.Invoke(this, Args);
        }

        /// <summary>Событие возникает при активности клавиатуры</summary>
        [Category("Keyboard"), Description("Событие возникает при активности клавиатуры")]
        public event OnKeyboardEventHandler KeyboardEvent;
        protected virtual void OnKeyboardEvent(OnKeyboardEventArgs Args)
        {
            //Обновление списка нажатых клавиш
            lock(f_PressedKeys)
                f_PressedKeys.CheckKey(Args.Message, Args.Struct.VirtualKeyCode);

            lock(f_PressedKeys)
            {
                if(Args.Message == WM.KEYDOWN
                    && !f_PressedKeys.Exists(K => K == Args.Struct.VirtualKeyCode))
                    f_PressedKeys.Add(Args.Struct.VirtualKeyCode);
                else if(Args.Message == WM.KEYUP)
                    f_PressedKeys.RemoveAll(K => K == Args.Struct.VirtualKeyCode);
            }


            var lv_Handlers = KeyboardEvent;
            if(lv_Handlers != null)
                lv_Handlers.Invoke(this, Args);

            switch(Args.Message)
            {
                case WM.KEYDOWN:
                    OnKeyDown(Args);
                    break;
                case WM.KEYUP:
                    OnKeyUp(Args);
                    break;
                case WM.SYSKEYDOWN:
                    OnSysKeyDown(Args);
                    break;
                case WM.SYSKEYUP:
                    OnSysKeyUp(Args);
                    break;
            }
        }
        private void OnKeyboardEvent(WM Message, Keyboard.KbDllHookStruct Struct)
        {
            OnKeyboardEvent(new OnKeyboardEventArgs(Message, Struct));
        }

        /// <summary>Событие возникает при включении и выключении объекта</summary>
        [Category("State")]
        [Description("Событие возникает при включении и выключении объекта")]
        public event EventHandler EnableChanged;
        protected virtual void OnEnableChanged(EventArgs Args)
        {
            var lv_Handlers = EnableChanged;
            if(lv_Handlers != null)
                lv_Handlers.Invoke(this, Args);
            OnPropertyChanged("Enable");
        }
        private void OnEnableChanged() { OnEnableChanged(EventArgs.Empty); }

        #endregion

        /* --------------------------------------------------------------------------------------------- */

        #region Поля

        private readonly KeyboardHookLL f_KeyboardHookLL = new KeyboardHookLL();

        private readonly Keyboard.KeyList f_PressedKeys = new Keyboard.KeyList();

        #endregion

        /* --------------------------------------------------------------------------------------------- */

        #region Свойства

        /// <summary>Состояние активности объекта</summary>
        [Category("State"), Description("Состояние активности объекта"), DefaultValue(false)]
        public bool Enable
        {
            get { return f_KeyboardHookLL.Enable; }
            set
            {
                if(Enable == value) return;
                f_PressedKeys.Clear();
                f_KeyboardHookLL.Enable = value;
                OnEnableChanged();
            }
        }

        /// <summary>Список нажатых клавиш</summary>
        [Description("Список нажатых клавиш")]
        public Keyboard.KeyList PressedKeys { get { return f_PressedKeys; } }

        #endregion

        /* --------------------------------------------------------------------------------------------- */

        #region Конструкторы

        public KeyboardComponent() : this(null) { }

        public KeyboardComponent(IContainer container)
        {
            if(container != null) container.Add(this);

            InitializeComponent();

            f_KeyboardHookLL.Event += Hook_EventHandler;
        }

        #endregion

        /* --------------------------------------------------------------------------------------------- */

        #region Методы

        protected virtual void Hook_EventHandler(object sender, HookEventArgs e)
        {
            var lv_Message = (WM)e.wParams;
            var lv_KeyStruct = e.lObject<Keyboard.KbDllHookStruct>();

            OnKeyboardEvent(lv_Message, lv_KeyStruct);
        }



        #endregion

        /* --------------------------------------------------------------------------------------------- */
    }
}
